package com.cmes.core;

import com.cmes.core.utils.CollectionUtils;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.common.collect.Maps;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;

/**
 * 分页辅助类
 *
 * @author Bruce.Gong
 * @since 1.0.0-SNAPSHOT
 */
@EqualsAndHashCode(callSuper = true)
@Getter
@Setter
@NoArgsConstructor
public class Pagination<T> extends Page<T> {

    /**
     * 总页数
     */
    private long totalPage;

    /**
     * 是否为第一页
     */
    private boolean isFirstPage = false;

    /**
     * 是否为最后一页
     */
    private boolean isLastPage = false;

    /**
     * 是否有前一页
     */
    private boolean hasPreviousPage = false;

    /**
     * 是否有下一页
     */
    private boolean hasNextPage = false;

    /**
     * 当前页面第一个元素在数据库中的行号
     */
    private long startRow;

    /**
     * 当前页面最后一个元素在数据库中的行号
     */
    private long endRow;

    /**
     * 聚合值，使用 {@link #stat(String, Object)} 进行添加
     */
    @JsonIgnore
    private Map<String, Object> map = Maps.newHashMapWithExpectedSize(4);

    @JsonAnyGetter
    private Map<String, Object> getMap() {
        return this.map;
    }

    public Pagination(int page, int pageSize) {
        super(page, pageSize);
    }

    public Pagination(int page, int pageSize, boolean searchCount) {
        super(page, pageSize, searchCount);
    }

    public Pagination(List<T> rows, long totalRow, int page, int pageSize) {
        super(page, pageSize);
        of(rows, totalRow);
    }

    /**
     * 查询数据后，传入当前页数据及总行数，自动计算其它属性
     * <p>
     * 在本操作之前，必须调用构造函数，设置 {@code page} 及 {@code pageSize} 属性
     *
     * @param rows     当前页数据
     * @param totalRow 总行数
     * @return Pagination
     */
    public Pagination<T> of(List<T> rows, long totalRow) {
        this.rows = CollectionUtils.isEmpty(rows) ? Collections.emptyList() : rows;
        int size = rows.size();
        this.totalRow = Math.max(totalRow, size);

        this.isFirstPage = false;
        this.isLastPage = false;
        this.hasPreviousPage = true;
        this.hasNextPage = true;

        this.totalPage = (int) Math.ceil(this.totalRow / (double) this.pageSize);
        if (this.pageNum == 1) {
            this.isFirstPage = true;
            this.hasPreviousPage = false;
        }
        if (this.pageNum >= this.totalPage) {
            this.isLastPage = true;
            this.hasNextPage = false;
        }

        this.startRow = (long) (this.pageNum - 1) * this.pageSize + 1;
        this.endRow = this.startRow + Math.min(size, this.pageSize);
        return this;
    }

    /**
     * 增加一个统计结果
     *
     * @param key   键
     * @param value 值
     * @return StatVO
     */
    public Pagination<T> stat(String key, Object value) {
        this.map.put(key, value);
        return this;
    }

    /**
     * 增加一个统计结果
     *
     * @param key   键
     * @param value 值
     * @return StatVO
     */
    @SuppressWarnings("unchecked")
    public <V> Pagination<T> merge(String key, V value, BiFunction<V, V, V> mergeFunction) {
        V ov = (V) this.map.get(key);
        V nv = ov == null ? value : mergeFunction.apply(ov, value);
        if (nv == null) {
            this.map.remove(key);
        } else {
            this.map.put(key, nv);
        }
        return this;
    }

}
